package com.tdcoding.chat.helper;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

public class ProtostuffHelper {
	private static final String TAG = "ProtostuffHelper";
	private static final int BUFFER_SIZE = 4096;
	private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();

	@SuppressWarnings("unchecked")
	private static <T> Schema<T> getSchema(Class<T> clazz) {
		Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
		if (schema == null) {
			schema = RuntimeSchema.getSchema(clazz);
			if (schema != null) {
				cachedSchema.put(clazz, schema);
			}
		}
		return schema;
	}

	/**
	 * 序列化
	 *
	 * @param obj
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> byte[] serialize(T obj) {
		Class<T> clazz = (Class<T>) obj.getClass();
		LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
		try {
			Schema<T> schema = getSchema(clazz);
			return ProtobufIOUtil.toByteArray(obj, schema, buffer);
		} catch (Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		} finally {
			buffer.clear();
		}
	}

	/**
	 * 反序列化
	 *
	 * @param data
	 * @param clazz
	 * @return
	 */
	public static <T> T deserialize(byte[] data, Class<T> clazz) {
		try {
			T obj = clazz.newInstance();
			Schema<T> schema = getSchema(clazz);
			ProtobufIOUtil.mergeFrom(data, obj, schema);
			return obj;
		} catch (Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		}
	}

	/**
	 * 反序列化
	 *
	 * @param data
	 * @param clazz
	 * @return
	 */
	public static <T> T deserialize(InputStream data, Class<T> clazz) {
		try {
			T obj = clazz.newInstance();
			Schema<T> schema = getSchema(clazz);
			ProtostuffIOUtil.mergeFrom(data, obj, schema);
			return obj;
		} catch (Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		}
	}

	public static <T> String serializableToString(T obj) throws Exception {
		byte[] bytes = serialize(obj);
		return byteToString(bytes);
	}
	
	public static <T> T stringToSerializable(String data, Class<T> clazz) throws Exception {
		InputStream inputStream = stringToInputStream(data);
		return deserialize(inputStream, clazz);
	}
	
	public static <T> String listToString(List<T> objs) throws Exception {
		StringBuffer sBuffer=new StringBuffer();
		for (int vari = 0; vari < objs.size(); vari++) {
			String serializable = serializableToString(objs.get(vari));
			sBuffer.append(serializable+"【|】");
		}
		return sBuffer.toString();
	}
	
	public static <T> List<T> stringToList(String data, Class<T> clazz) throws Exception {
		List<T> objs=new ArrayList<T>();
		String[] deserializes=data.split("【|】");
		for (String deserialize : deserializes) {
			T t=stringToSerializable(deserialize, clazz);
			objs.add(t);
		}
		return objs;
	}

	/**
	 * 将InputStream转换成String
	 * 
	 * @param in
	 *            InputStream
	 * @return String
	 * @throws Exception
	 * 
	 */
	public static String inputStreamToString(InputStream in) throws Exception {
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] data = new byte[BUFFER_SIZE];
		int count = -1;
		while ((count = in.read(data, 0, BUFFER_SIZE)) != -1)
			outStream.write(data, 0, count);
		data = null;
		String result=new String(outStream.toByteArray(), "ISO-8859-1");
		outStream.flush();
		outStream.close();
		return result;
	}

	/**
	 * 将InputStream转换成某种字符编码的String
	 * 
	 * @param in
	 * @param encoding
	 * @return
	 * @throws Exception
	 */
	public static String inputStreamToString(InputStream in, String encoding) throws Exception {
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] data = new byte[BUFFER_SIZE];
		int count = -1;
		while ((count = in.read(data, 0, BUFFER_SIZE)) != -1)
			outStream.write(data, 0, count);
		data = null;
		String result=new String(outStream.toByteArray(), encoding);
		outStream.flush();
		outStream.close();
		return result;
	}

	/**
	 * 将String转换成InputStream
	 * 
	 * @param in
	 * @return
	 * @throws Exception
	 */
	public static InputStream stringToInputStream(String in) throws Exception {
		ByteArrayInputStream is = new ByteArrayInputStream(in.getBytes("ISO-8859-1"));
		return is;
	}

	/**
	 * 将InputStream转换成byte数组
	 * 
	 * @param in
	 *            InputStream
	 * @return byte[]
	 * @throws IOException
	 */
	public static byte[] inputStreamToByte(InputStream in) throws IOException {
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] data = new byte[BUFFER_SIZE];
		int count = -1;
		while ((count = in.read(data, 0, BUFFER_SIZE)) != -1)
			outStream.write(data, 0, count);
		data = null;
		byte[] bytes=outStream.toByteArray();
		outStream.flush();
		outStream.close();
		return bytes;
	}

	/**
	 * 将byte数组转换成InputStream
	 * 
	 * @param in
	 * @return
	 */
	public static InputStream byteToInputStream(byte[] in) throws Exception {
		ByteArrayInputStream is = new ByteArrayInputStream(in);
		return is;
	}

	/**
	 * 将byte数组转换成String
	 * 
	 * @param in
	 * @return
	 * @throws Exception
	 */
	public static String byteToString(byte[] in) throws Exception {
		InputStream is = byteToInputStream(in);
		return inputStreamToString(is);
	}

	// public static <T> void main(String[] args) {
	// List<ChannelInfo> channelInfos=new ArrayList<ChannelInfo>();
	// for (int vari = 0; vari <100; vari++) {
	// ChannelInfo channelInfo=new ChannelInfo();
	// channelInfo.setUserId(10000000000000l+vari);
	// channelInfo.setPlatform(vari);
	// channelInfo.setNickName("channelInfo"+vari);
	// channelInfo.setPassword("111111111111111");
	// channelInfos.add(channelInfo);
	// TestInfo testInfo=new TestInfo();
	// testInfo.setUserId(20000000000000l+vari);
	// testInfo.setPlatform(20+vari);
	// testInfo.setNickName("testInfo"+vari);
	// testInfo.setPassword("22222222222222");
	// channelInfo.setTestInfo(testInfo);
	// }
	// List<byte[]> serializers=serializeToList(channelInfos);
	// Logger.println("main","serializeToList",serializers.size());
	// List<ChannelInfo> deserializers=deserializeToList(serializers,
	// ChannelInfo.class);
	// Logger.println("main", "deserializeToList",deserializers);
	// }
}
